AWS Batchを使って5分以上かかる処理を実行してみる
どうも!西村祐二@大阪です。
Lambdaは現在(2017/8/17)実行時間が最長5分までという制限があります。 そこで、今回はAWS Batchを使って5分以上かかる処理を実行させてみたいと思います。
AWS Batchとは
JobをAWS Batchになげると予め設定しておいたインスタンスを起動し、 ECRまたはDocker Hubからコンテナイメージを取得しタスクを実行してくれます。 また、実行しているタスクがない場合インスタンスを起動してから1時間以内に自動で削除してくれます。
やりたいこと
・CodeCommitに置いてる5分以上かかるスクリプトをコマンド1つで実行したい ・構成はなるべくシンプルにしたい
5分以上かかる処理
今回は例として1分毎にS3に空ファイルを作成する処理を実行してみたいと思います。 下記2つのファイルをCodeCommitの「test-commit」レポジトリにおいています。
run.sh
の中身が下記になります。
pythonのプログラムをたたいているだけです。
パスは適宜変更してください。
コンテナ上で実行されることを想定して、絶対パスで記載するのがよいかと思います。
#!/bin/bash python /usr/local/test-batch/test.py
test.py
の中身が下記になります。
sleepを使って60秒毎にS3の「test-bucket-batch」の中に
ファイルを作成する処理を10回繰り返しています。
つまり約10分かかる想定です。
※試す場合はS3のバケット名など変数やパラメータを適宜変更してください。
# -*- coding: utf-8 -*- import boto3 from time import sleep # S3 BUCKET_NAME = "test-bucket-batch" TARGET_DIR = "batch" FILE_CONTENTS = "" # Timer MAX_ITER = 10 SEC = 60 def PutS3(i): s3 = boto3.resource('s3') bucket = s3.Bucket(BUCKET_NAME) filename = str(i+1) obj = bucket.put_object(ACL='private', Body=FILE_CONTENTS, Key=TARGET_DIR + "/" + filename, ContentType='text/plain') return str(obj) def count(i): print("{}秒経過しました。".format((i+1)*SEC)) if __name__ == '__main__': for i in range(MAX_ITER): sleep(SEC) count(i) PutS3(i)
この処理をAWS Batchを使ってコンテナ上で実行してみましょう。
構成図
今回想定する構成は下記画像のようになります。 ユーザはCodeCommitでソースを管理しており、処理を実行したいときに AWS BatchにJobをなげると自動で実行してくれて、 かつ、自動でインスタンスを削除してくれます。
作業概要
・コンテナイメージを作る ・ECRにコンテナイメージを登録する ・AWS Batchを設定する ・AWS Batchを実行してみる ・CLIから実行してみる
事前準備
・上記記載のコードをCodeCommitで管理しておいてください ・Dockerコマンドが使える環境を用意しておいてください ・aws-cliを使えるようにしておいてください
コンテナイメージを作る
今回の処理を実行するためには大まかに下記の環境がコンテナ内に必要となります。
・python ・git ・awscli ・boto3 ・Codecommitからcloneしたソース中にあるrun.shをキックするスクリプト
AWS Batchで使用するコンテナイメージはECRとDocker Hubから取得できます。 もし、Docker Hubで要件にあうコンテナイメージがあればコンテナイメージを作成する必要はありません。
CentOSのコンテナイメージをベースに作成する
予めDockerコマンドを使えるようにしておいてください。 私の場合はDocker for Macを使ってMac上でコンテナイメージを作成しています。
今回はオフィシャルで提供されているCentOSのコンテナをベースにして作成していきます。
//もととなるコンテナイメージをpull $ docker pull centos //コンテナイメージを確認する $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos latest xxxxxxxxxxxx xx days ago 193MB //コンテナの中に入る $ docker run -it centos /bin/bash
次からコンテナの中での作業です。
//pipコマンドインストール [root@xxx]# curl -kl https://bootstrap.pypa.io/get-pip.py | python //aws-cliインストール [root@xxx]# pip install awscli //boto3インストール [root@xxx]# pip install boto3 //gitインストール [root@xxx]# yum install -y git //CodeCommitからcloneして中のrun.shをキックするスクリプト [root@xxx]# vi /usr/local/init.sh --------- #!/bin/bash CODE_REPO=https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/ CODE_DIR=/usr/local/test-batch git clone --config credential.helper='!aws --region ap-northeast-1 codecommit credential-helper $@' --config credential.UseHttpPath=true $CODE_REPO $CODE_DIR sh /usr/local/test-batch/run.sh ---------- [root@xxx]# exit
「CodeCommitからcloneして中のrun.shをキックするスクリプト」について少し解説しておきます。
このスクリプトでポイントとなるところとしてはgit clone
のconfigのところです。
通常、cloneするときのアクセスキーのやり取りや管理を考える必要があるのですが、
今回はコンテナ起動にIAMロールを付与しSSHの鍵もアクセスキーも不要でcloneできるようにしています。
詳細は下記ブログが大変参考になります!
さて、現状ローカルに戻ってきています。先程作業した状態のコンテナイメージを作成しましょう。
//作業時のコンテナIDを確認 $ docker ps -a -n=5 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES XXXXxxxxxxxx centos "/bin/bash" 9 seconds ago Exited (0) 1 second ago elated_hypatia ・ ・ ・ //「test-container」としてコンテナイメージ作成 $ docker commit XXXXxxxxxxxx test-container //イメージが作られたことを確認 $ docker images //コマンドが使えること、ファイルがあることを確認 $ docker run -it test-container /bin/bash
ちなみにDockerfileで表すとおそらくこんな感じです。
FROM centos:latest RUN curl -kl https://bootstrap.pypa.io/get-pip.py | python RUN pip install awscli RUN pip install boto3 RUN yum install -y git ADD init.sh /usr/local/
CMDはAWS Batchの「Job definitions」のときに設定できるのでイメージを作成するときには不要です。
ECRにコンテナイメージを登録する
マネージメントコンソールから「EC2 Container Service」にアクセスします。
コンテナイメージを登録するリポジトリ名を「test-repo」として「次のステップ」をクリックします。
リポジトリが正常に作成されたため「キャンセル」をクリックします。
リポジトリをクリックし、「test-repo」が作成されていることを確認します。
表示されたコマンドを参考に作成したコンテナイメージをECRに登録します。
$ aws ecr get-login --no-include-email --region ap-northeast-1 docker login ・・・・・・・・・ ながいログインコマンドが出力されます。 //出力されたコマンドを実行します。 $ docker login -u AWS -p <出力された値> https://xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com Login Succeeded //作成したコンテナイメージ「test-container」をもとにタグをつけます $ docker tag test-container:latest xxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test-repo:latest //リポジトリにpushします $ docker push xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test-repo:latest
コンテナイメージが登録されたか確認します。
AWS Batchを設定する
はじめてAWS Batchを行う際に表示される「初期設定ウィザード」はそのまま進めて行き、 デフォルトのままとりあえず作成するのが良いと思います。 そうすることで、ECSに設定するroleやVPCの環境など自動的に作成してくれます。 デフォルトで作成後にあらためて個別で設定していくほうがスムーズに行くかと思います。 デフォルトのまま作成すると、おそらく下記環境が作成されているかと思います。 また、Jobが実行されインスタンスが立ち上がっていたら削除しておいてください。
■Batch ・Job definitions ・Job queues ・Compute environments ■ロール ・AWSBatchServiceRole ・ecsInstanceRole ■VPCの環境
Compute environmentsの設定
デフォルトで作成されたのとは別で 「test-env」として起動するインスタンスの制御ができるCompute environmentsを設定しました。
・「Desired vCPUs」は起動するインスタンスの希望するvCPUの数なので画像では0となっていますが、「1」や「2」としておくほうが良いかと思います。
・「Minimum vCPUs」を0に設定しておくと、起動したインスタンスを自動的にterminatedしてくれます。 terminatedされるタイミングとして、実行中のジョブがないかつ、インスタンスが起動して1時間たたないぐらいにterminatedされるようです。 ※Provisioning Modelを「On-demand」であっても、自動的にterminatedされていました。
・今回の処理ではCodeCommitとS3にアクセスするため、Instance roleの「ecsInstanceRole」に権限を付与しておきます。
また、「Compute environments」を作成すると自動的にECSのクラスターが作成されます。裏ではECSで動いているのでAWS Batchでタスクを実行しているとこちらにも表示されます。
Job queues
デフォルトで作成されたのとは別で 名前を「test-job-queues」とし、環境を先程作成した「test-env」としてqueuesを作成しました。
Job definitions
デフォルトで作成されたのとは別で
名前を「test-job-def」とし、コンテナイメージをECRに登録しているコンテナを指定
コンテナがrunされたときにCodeCommitからソースをcloneして処理を実行したいため
コンテナ内に設置したinit.sh
を実行するように指定します。
他はデフォルトまま設定しています。
「Command」で指定したコマンドは Docker CMD パラメータに相当します。 つまり、Docker runされるときに指定したコマンドが実行されるため 下記のような流れで処理が進みます。
インスタンスに作成したコンテナイメージがロードされDocker runされる
↓
コンテナ内に設置したinit.sh
を実行される
↓
CodeCommitからソースをcloneして、中にあるrun.sh
を実行される
↓
1分毎にS3にファイルを作成する処理が実行される
AWS Batchを実行してみる
名前を「test-job」とし、先程作成した「Job definitions」「Job queues」を指定して「Submit job」をクリックします。 するとCodeCommit内のプログラムが実行されます。
問題なく処理が完了したらS3に下記画像のように空のファイルが10個作成されます。
また、AWS BatchのJobのStatusがSUCCEEDEDになっているはずです。
■全体の流れとして下記のように進むと思われます。
AWS Batchにjobがsubmitされる
↓
インスタンスが起動する
↓
インスタンスにコンテナイメージがロードされる
↓
Job definitionsで指定したコマンドによってコンテナ内に設置したinit.sh
を実行される
↓
CodeCommitからソースをcloneして、中にあるrun.sh
を実行する
↓
1分毎にS3にファイルを作成する処理が実行される
↓
処理が終わると自動的にインスタンスがterminatedされる
CLIから実行
CLIからAWS BatchにjobをSubmitするには下記の形式でコマンドを実行するればSubmitするができます。
aws --profile --region batch submit-job --job-name <名前> --job-queue --job-definition <定義したjob>
下記が今回作成した設定を用いてJobをAWS BatchにSubmitするコマンド例です。 Job名を「test-cli」としています。
$ aws --profile default --region ap-northeast-1 batch submit-job --job-name test-cli --job-queue "test-job-queues" --job-definition test-job-def:1 aaaaaa-bbbbb-ccccc-dddd-eeeeeee test-cli
マネージメントコンソール上からでもJobがSubmitされて実行されていることがわかります。
さいごに
AWS Batchは動かしたいプログラムが動作する環境と プログラムを実行するスクリプトを含めたコンテナイメージを作成させすれば CodeCommitからcloneし、プログラムを実行、 処理が終わったら自動的にインスタンスを削除するようにできるため 運用やコストの観点で有効なサービスではないでしょうか。
今回触れていませんが、AWS Batchはあの処理が終わってからこの処理を実行したいなど依存関係も設定できるので 使いこなせればより有効に使えそうです。
誰かの参考になれば幸いです。